package com.lagou.edu.wwy.mvcframework.servlet;

import com.lagou.edu.wwy.mvcframework.annotation.*;
import com.lagou.edu.wwy.mvcframework.pojo.Handler;
import com.lagou.edu.wwy.mvcframework.pojo.HandlerInterceptor;
import com.lagou.edu.wwy.mvcframework.pojo.SecurityHandlerInterceptor;
import org.apache.commons.lang3.StringUtils;

import javax.servlet.*;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class LgDispatcherServlet extends HttpServlet {
    private Properties properties = new Properties();
    private List<String> classNames = new ArrayList<String>();
    private Map<String, Object> ioc = new HashMap<String, Object>();
    private List<Handler> handlerMappings = new ArrayList<>();


    public void init(ServletConfig servletConfig) throws ServletException {
        //1,加载配置文件
        String contextConfigLocation = servletConfig.getInitParameter("contextConfigLocation");
        doLoadConfig(contextConfigLocation);
        //2.扫描包下相关的类
        doScan(properties.getProperty("basePackage"));
        //3.初始化bean
        doInstance();
        //4.实现依赖注入
        doAutowired();
        //5.初始化handlerMapping
        initHandlerMapping();
        System.out.println("初始化完成");

    }

    private void initHandlerMapping() {
        if (this.ioc.isEmpty()) {
            return;
        }
        //认证拦截器
        HandlerInterceptor securityHandlerInterceptor = (HandlerInterceptor) ioc.get(SecurityHandlerInterceptor.class.getName());
        for (Map.Entry<String, Object> entry : this.ioc.entrySet()) {
            Object controller = entry.getValue();
            Class<?> aClass = controller.getClass();
            if (!aClass.isAnnotationPresent(LagouController.class)) {
                continue;
            }
            Method[] methods = aClass.getMethods();
            if (methods.length == 0) {
                continue;
            }

            String baseUrl = "/";

            if (aClass.isAnnotationPresent(LagouRequestMapping.class)) {
                LagouRequestMapping requestMapping = aClass.getAnnotation(LagouRequestMapping.class);
                baseUrl += requestMapping.value();
            }
            //判断是否添加了认证拦截器
            Security security = null;
            if (aClass.isAnnotationPresent(Security.class)) {
                security = aClass.getAnnotation(Security.class);
            }
            for (Method method : methods) {
                String methodUrl = "/";
                if (method.isAnnotationPresent(LagouRequestMapping.class)) {
                    LagouRequestMapping requestMapping = method.getAnnotation(LagouRequestMapping.class);
                    methodUrl += requestMapping.value();
                }

                String url = (baseUrl + methodUrl).replaceAll("//", "/");
                Parameter[] parameters = method.getParameters();


                Handler handler = new Handler(controller, method, Pattern.compile(url));
                for (int i = 0; i < parameters.length; i++) {
                    //这里不针对request和response 对象名称进行特殊处理 了，在调用handler的时候再对参数进行特殊处理
                    Parameter parameter = parameters[i];
                    handler.getParamIndexMapping().put(parameter.getName(), i);
                }

                if (aClass.isAnnotationPresent(Security.class)) {
                    security = aClass.getAnnotation(Security.class);
                }
                if ( security != null) {
                    handler.getInterceptors().add(securityHandlerInterceptor);
                }
                handlerMappings.add(handler);
            }


        }
    }

    /**
     * 实现依赖注入
     */
    private void doAutowired() {
        for (Object obj : ioc.values()) {
            Class<?> aClass = obj.getClass();
            Field[] fields = aClass.getDeclaredFields();
            for (Field field : fields) {
                if (!field.isAnnotationPresent(LagouAutowired.class)) {
                    continue;
                }
                Class<?> fieldClass = field.getType();
                LagouAutowired annotation = field.getDeclaredAnnotation(LagouAutowired.class);
                String id = annotation.value();
                if (id == null || "".equals(id)) {
                    id = fieldClass.getName();
                }
                field.setAccessible(true);
                try {
                    Object fileValue = ioc.get(id);
                    field.set(obj, fileValue);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 初始化所有扫描出来的类
     */
    private void doInstance() {
        for (String className : classNames) {
            try {
                Class<?> aClass = Class.forName(className);

                //自定义的bean id
                String customId = "";
                if (aClass.isAnnotationPresent(LagouController.class)) {
                    LagouController annotation = aClass.getAnnotation(LagouController.class);
                    customId = annotation.value();
                } else if (aClass.isAnnotationPresent(LagouService.class)) {
                    LagouService annotation = aClass.getAnnotation(LagouService.class);
                    customId = annotation.value();
                } else {
                    continue;
                }
                //ioc中维护的对象是单例的
                Object instance = aClass.newInstance();
                //考虑到可能根据类型注入
                ioc.put(aClass.getName(), instance);
                //id
                if (customId == null || "".equals(customId)) {
                    ioc.put(lowerFirst(aClass.getSimpleName()), instance);
                } else {
                    ioc.put(customId, instance);

                }
                for (Class<?> anInterface : aClass.getInterfaces()) {
                    //如果实现了接口，则以接口名称保存一份（注意：这里暂不考虑同一个接口有多个实现类的场景）
                    ioc.put(anInterface.getName(), instance);

                }

            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    private String lowerFirst(String str) {
        char[] chars = str.toCharArray();
        if ('A' <= chars[0] && chars[0] <= 'Z') {
            chars[0] += 32;
            return String.valueOf(chars);
        }
        return str;
    }


    /**
     * 扫描报下的所以有类
     *
     * @param basePackage
     */
    private void doScan(String basePackage) {
        String scanPackagePath = Thread.currentThread().getContextClassLoader().getResource("").getPath() + basePackage.replaceAll("\\.", "/");
        //这种方式应该只能加载class文件，如果是jar的形式，应该加载不了（待验证）
        File pack = new File(scanPackagePath);
        File[] files = pack.listFiles();
        for (File file : files) {
            if (file.isDirectory()) {
                doScan(basePackage + "." + file.getName());
            } else if (file.getName().endsWith(".class")) {
                String className = basePackage + "." + file.getName().replace(".class", "");
                classNames.add(className);
            }
        }
    }

    private void doLoadConfig(String contextConfigLocation) throws ServletException {
        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);
        try {
            properties.load(resourceAsStream);
        } catch (IOException e) {
            System.out.println("加载配置失败");
            throw new ServletException(e);
        }
    }


    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setHeader("content-type", "text/html;charset=UTF-8");
        resp.setCharacterEncoding("utf-8");
        //获取请求url 根据url获取对应的handler

        Handler handler = getHandler(req);
        if (handler == null){
            resp.setStatus(404);
            resp.getWriter().write("not found");
            return;
        }
        //拦截器
        for (HandlerInterceptor interceptor : handler.getInterceptors()) {
            if (!interceptor.preHandler(req,resp,handler)){
                System.out.println("拦截器处理不通过");
                return;
            }
        }
        //参数处理

        Method method = handler.getMethod();
        Class<?>[] parameterTypes = method.getParameterTypes();

        Map<String, Integer> paramIndexMapping = handler.getParamIndexMapping();
        Map<String, String[]> parameterMap = req.getParameterMap();
        Object[] args = new Object[parameterTypes.length];
        for (Map.Entry<String, Integer> entry : paramIndexMapping.entrySet()) {
            int index = entry.getValue();
            Class<?> type = parameterTypes[index];
            //这里根据类型注入 request 和response,session等
            if (HttpServletRequest.class.isAssignableFrom(type)) {
                args[index] = req;
            } else if (HttpServletResponse.class.isAssignableFrom(type)) {
                args[index] = resp;
            } else if (HttpSession.class.isAssignableFrom(type)) {
                args[index] = req.getSession();
            } else {
                //这里暂时都当做字符串处理
                args[index] = StringUtils.join(parameterMap.get(entry.getKey()), ",");
            }
        }
        try {
            //调用handler
            Object result = method.invoke(handler.getController(),args);
            if (result != null){
                //将返回信息返回前段
                resp.getWriter().write(result.toString());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }


    }

    private Handler getHandler(HttpServletRequest req) {
        if (this.handlerMappings == null || this.handlerMappings.isEmpty()) {
            return null;
        }
        String uri = req.getRequestURI().replaceAll("//", "/");
        for (Handler mapping : this.handlerMappings) {
            Matcher matcher = mapping.getPattern().matcher(uri);
            if (matcher.matches()) {
                return mapping;
            }
        }
        return null;


    }
}
